

#ifndef RTMESSAGEBLOCK_H
#define RTMESSAGEBLOCK_H

#include "RtReferenceControl.h"
#include "RtStdCpp.h"
#include "RtDebug.h"
#include "RtDataBlock.h"

/**
 * The concept of <CRtMessageBlock> is mainly copyed by <ACE_Message_Block>
 * http://www.cs.wustl.edu/~schmidt/ACE.html
 *
 * An <CRtMessageBlock> is modeled after the message data
 * structures used in System V STREAMS.  Its purpose is to
 * enable efficient manipulation of arbitrarily-large messages
 * without incurring much memory copying overhead.  Here are the
 * main characteristics of an <CRtMessageBlock>:
 * 1. Contains a pointer to a reference-counted
 *    <CRtDataBlock>, which in turn points to the actual data
 *    buffer.  This allows very flexible and efficient sharing of
 *    data by multiple <CRtMessageBlock>s.
 * 2. One or more <CRtMessageBlock> can be linked to form a
 *    'fragment chain.'
 *
 *	The internal structure of <CRtMessageBlock>:
 *     |<----------------------(Chained CRtMessageBlock)---------------------->|
 *     |<---(TopLevel CRtMessageBlock)--->|           | (Next CRtMessageBlock) |
 *     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (m_pNext) xxxxxxxxxxxxxxxxxxxxxxxxxx
 *     ^           ^          ^           ^---------->^
 *     |           |          |           |
 *     m_pBeginPtr m_pReadPtr m_pWritePtr m_pEndPtr 
 *                 |<-Length->|<--Space-->|
 *     |<------Capacity------------------>|
 *
 *  If <m_Flag> is set with <DONT_DELETE>, <m_pBeginPtr> and <m_pEndPtr> are  
 *  pointing to the actual data while <m_pDataBlock> is NULL.
 */
class RT_API_EXPORT CRtMessageBlock 
{
public:
	enum 
	{
		/// Don't delete the data on exit since we don't own it.
		DONT_DELETE = 1 << 0,

		/// Malloc and copy data internally
		MALLOC_AND_COPY = 1 << 1,

		/// Can't read/write.
		READ_LOCKED = 1 << 8,
		WRITE_LOCKED = 1 << 9,

		/// the following flags are only for internal purpose.
		INTERNAL_MASK = 0xFF00,
		DUPLICATED = 1 << 17
	};
	typedef DWORD MFlag;
	
	/**
	 * Create an initialized message containing <aSize> bytes. 
	 * AdvanceTopLevelWritePtr for <aAdvanceWritePtrSize> bytes.
	 * If <aData> == 0 then we create and own the <aData>;
	 * If <aData> != 0 
	 *   If <aFlag> == DONT_DELETE, do nothing when destruction;
	 *   Else delete it when destruction;
	 */
	explicit CRtMessageBlock(
		DWORD aSize, 
		LPCSTR aData = NULL, 
		MFlag aFlag = 0, 
		DWORD aAdvanceWritePtrSize = 0);
	
	explicit CRtMessageBlock(CRtDataBlock *aDb, MFlag aFlag);

	~CRtMessageBlock();

	/// Read <aCount> bytes, advance it if <aAdvance> is TRUE,
	/// if <aDst> != NULL, copy data into it.
	RtResult Read(LPVOID aDst, DWORD aCount, DWORD *aBytesRead = NULL, BOOL aAdvance = TRUE);
	
	//peek some data from messageblock.
	RtResult Peek(LPVOID aDst, DWORD aCount, DWORD aPos = 0, DWORD* aBytesRead = NULL);

	/// Write and advance <aCount> bytes from <aSrc> to the top-level <CRtMessageBlock>
	RtResult Write(LPCVOID aSrc, DWORD aCount, DWORD *aBytesWritten = NULL);

	/// Get the length of the <CRtMessageBlock>s, including chained <CRtMessageBlock>s.
	DWORD GetChainedLength() const ;
	
	/// Get the space of the <CRtMessageBlock>s, including chained <CRtMessageBlock>s.
	DWORD GetChainedSpace() const ;

	// Don't export <CRtDataBlock> because it maybe realloc when AddReference(),
	// the Base() is not the same as previous one.
	//CRtDataBlock* GetDataBlock();

	// Append <aMb> to the end chain.
	void Append(CRtMessageBlock *aMb);

	// Get the next <CRtMessageBlock>
	CRtMessageBlock* GetNext();

	/// Advance <aCount> bytes for reading in chained <CRtMessageBlock>s.
	RtResult AdvanceChainedReadPtr(DWORD aCount, DWORD *aBytesRead = NULL);

	/// Advance <aCount> bytes for writing in chained <CRtMessageBlock>s.
	/// <CRtMessageBlock> must never be read before, and could write continually.
	RtResult AdvanceChainedWritePtr(DWORD aCount, DWORD *aBytesWritten = NULL);

	/// Return a "shallow" copy that not memcpy actual data buffer.
	/// Use DestroyChained() to delete the return <CRtMessageBlock>.
	CRtMessageBlock* DuplicateChained();

	/// Disjoint the chained <CRtMessageBlock>s at the start point <aStart>.
	/// return the new <CRtMessageBlock> that advanced <aStart> read bytes from the old.
	/// <aStart> must be less than ChainedLength.
	/// Use DestroyChained() to delete the return <CRtMessageBlock>.
	CRtMessageBlock* Disjoint(DWORD aStart);

	/**
	 * Decrease the shared CRtDataBlock's reference count by 1.  If the
	 * CRtDataBlock's reference count goes to 0, it is deleted.
	 * In all cases, this CRtMessageBlock is deleted - it must have come
	 * from the heap, or there will be trouble.
	 *
	 * DestroyChained() is designed to DestroyChained the continuation chain; the
	 * destructor is not.  If we make the destructor DestroyChained the
	 * continuation chain by calling DestroyChained() or delete on the message
	 * blocks in the continuation chain, the following code will not
	 * work since the message block in the continuation chain is not off
	 * the heap:
	 *
	 *  CRtMessageBlock mb1(1024);
	 *  CRtMessageBlock mb2(1024);
	 *
	 *  mb1.SetNext(&mb2);
	 *
	 * And hence, call DestroyChained() on a dynamically allocated message
	 * block. This will DestroyChained all the message blocks in the
	 * continuation chain.  If you call delete or let the message block
	 * fall off the stack, cleanup of the message blocks in the
	 * continuation chain becomes the responsibility of the user.
	 *
	 * None <CRtMessageBlock> but returned by DuplicateChained() or 
	 * Disjoint() could be destroyed by this DestroyChained().
	 */
	void DestroyChained();

	/// Fill iovec to make socket read/write effectively.
	DWORD FillIov(
		iovec* inIov, 
		DWORD inMax, 
		DWORD& outFillLength, 
		const CRtMessageBlock*& outRemainder) const;

	/// For enhanced checking, mainly for debug purpose.
	void LockReading();
	void LockWriting();

	/// Destory chained <CRtMessageBlock>s whose length is 0.
	/// return the number of destroyed <CRtMessageBlock>.
	CRtMessageBlock* ReclaimGarbage();

	/// Copy all chained data.
	CRtString FlattenChained();

public:
	/// Get <m_pReadPtr> of the top-level <CRtMessageBlock>
	LPCSTR GetTopLevelReadPtr() const ;
	/// Advance <aStep> bytes from <m_pReadPtr> of the top-level <CRtMessageBlock>
	RtResult AdvanceTopLevelReadPtr(DWORD aStep);

	/// Get <m_pWritePtr> of the top-level <CRtMessageBlock>
	LPSTR GetTopLevelWritePtr() const ;
	/// Advance <aStep> bytes from <m_pWritePtr> of the top-level <CRtMessageBlock>
	RtResult AdvanceTopLevelWritePtr(DWORD aStep);

	/// Message length is (<m_pWritePtr> - <m_pReadPtr>).
	/// Get the length in the top-level <CRtMessageBlock>.
	DWORD GetTopLevelLength() const ;

	/// Get the number of bytes available after the <m_pWritePtr> in the top-level <CRtMessageBlock>.
	DWORD GetTopLevelSpace() const ;

	/// Rewind <m_pReadPtr> of chained <CRtMessageBlock>s to their beginnings,
	/// It's not safe because it don't record first read ptr if it not equals <m_pBeginPtr>.
	void RewindChained(BOOL bReadWind);
	void SaveChainedReadPtr();
	/// Return a "shallow" copy of the top-level <CRtMessageBlock>,
	/// if flag set DONT_DELETE, malloc and memcpy actual data,
	/// else just does CRtDataBlock::AddReference().
	CRtMessageBlock* DuplicateTopLevel() const ;

	static CRtString GetBlockStatics();

private:
	void Reset(CRtDataBlock *aDb);

private:
	CRtMessageBlock *m_pNext;
	CRtAutoPtr<CRtDataBlock> m_pDataBlock;
	LPCSTR m_pReadPtr;
	LPSTR m_pWritePtr;
	LPCSTR m_pSaveReadPtr;
	LPCSTR m_pBeginPtr;
	LPCSTR m_pEndPtr;
	MFlag m_Flag;

	static INT   s_block_createcount;
	static INT   s_block_destoycount;
private:
	// = Prevent assignment and initialization.
	CRtMessageBlock(const CRtMessageBlock &);
	void operator = (const CRtMessageBlock&);
};


// inline functions.
inline void CRtMessageBlock::Reset(CRtDataBlock *aDb)
{
	m_pDataBlock = aDb;
	m_pBeginPtr = m_pDataBlock ? m_pDataBlock->GetBasePtr() : NULL;
	m_pReadPtr = m_pBeginPtr;
	m_pWritePtr = const_cast<LPSTR>(m_pBeginPtr);
	m_pEndPtr = m_pBeginPtr + (m_pDataBlock ? m_pDataBlock->GetLength() : (DWORD)0);
}

inline DWORD CRtMessageBlock::GetTopLevelLength() const
{
	RT_ASSERTE(m_pWritePtr >= m_pReadPtr);
	return (DWORD)(m_pWritePtr - m_pReadPtr);
}

inline DWORD CRtMessageBlock::GetTopLevelSpace() const 
{
	RT_ASSERTE(m_pEndPtr >= m_pWritePtr);
	return (DWORD)(m_pEndPtr - m_pWritePtr);
}

inline CRtMessageBlock* CRtMessageBlock::GetNext()
{
	return m_pNext;
}

inline LPCSTR CRtMessageBlock::GetTopLevelReadPtr() const 
{
	RT_ASSERTE(RT_BIT_DISABLED(m_Flag, READ_LOCKED));
	return m_pReadPtr;
}

inline LPSTR CRtMessageBlock::GetTopLevelWritePtr() const 
{
	RT_ASSERTE(RT_BIT_DISABLED(m_Flag, WRITE_LOCKED));
	return m_pWritePtr;
}

inline RtResult CRtMessageBlock::AdvanceTopLevelWritePtr(DWORD aStep)
{
	RT_ASSERTE(RT_BIT_DISABLED(m_Flag, WRITE_LOCKED));
	RT_ASSERTE_RETURN(m_pWritePtr + aStep <= m_pEndPtr, RT_ERROR_NOT_AVAILABLE);
	m_pWritePtr += aStep;
	return RT_OK;
}

inline RtResult CRtMessageBlock::AdvanceTopLevelReadPtr(DWORD aStep)
{
	RT_ASSERTE(RT_BIT_DISABLED(m_Flag, READ_LOCKED));
	RT_ASSERTE_RETURN(m_pWritePtr >= m_pReadPtr + aStep, RT_ERROR_NOT_AVAILABLE);
	m_pReadPtr += aStep;
	return RT_OK;
}

inline void CRtMessageBlock::LockReading()
{
	RT_SET_BITS(m_Flag, READ_LOCKED);
}

inline void CRtMessageBlock::LockWriting()
{
	RT_SET_BITS(m_Flag, WRITE_LOCKED);
}


// Design Note:
// 1. <CRtDataBlock>* instead <CRtDataBlock> :
//    <CRtMessageBlock> can DuplicateChained without copying <CRtDataBlock>.
//
// 2. <CRtMessageBlock>* m_pNext instead <CRtDataBlock> list :
//    There is no offset in <CRtDataBlock>.
//
// 3. <CRtMessageBlock> has ReferenceControl :
//    Easy to add in list.
//    Need thread mutex when do ReferenceControl.
//    but how to deal with Func(const CRtMessageBlock &aMb)?
//    we can't invoke Write, Read or Advance in Func().
//    so we have to add a function DuplicateChained?
//
// 4. SendData(const CRtMessageBlock &aMb);
//    How do do ReferenceControl?
//
// 5. OnReceive(CRtMessageBlock &aMb);

#endif // !RTMESSAGEBLOCK_H
